﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;


namespace NetMoveCwS
{
    public enum SendDataType { SDT_CONTROL, SDT_GAMEDATA, SDT_LISTPLAYERS };

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    //------------------------------------//
    //------ Struct for sending ----------//
    //------------------------------------//
    public struct SendData
    {
        public SendDataType sdt;
        public int id_player;
        public int x;
        public int y;
        public int count_player;
        public int bird;
        public int reserved1;
        public int reserved2;

        public byte[] ToBytes()
        {
            Byte[] bytes = new Byte[Marshal.SizeOf(typeof(SendData))];
            GCHandle pinStructure = GCHandle.Alloc(this, GCHandleType.Pinned);
            try
            {
                Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
                return bytes;
            }
            finally
            {
                pinStructure.Free();
            }
        }
    }
    public struct player
    {
        public int x;
        public int y;
        public string id_md5;
        public int bird;

    }

    public class NetServer
    {
        
        public Socket Handle;
        public const int MAX_PLAYERS = 12;
        public Socket[] PlayersSocketHandles = new Socket[MAX_PLAYERS];
        public bool[] PlayersActive = new bool[MAX_PLAYERS];
        public bool[] PlayersInitilized = new bool[MAX_PLAYERS];
        public int[] PlayersID = new int[MAX_PLAYERS];
        public string GetMD5(string input);
        public string[] PlayersID_MD5 = new string[MAX_PLAYERS];

        public int PlayersActiveCount = 0;
        public bool Started = false;
        public IPAddress[] IP4HostList;


        IPAddress ipAddr;
        IPEndPoint ipEndPoint;
        Socket sListener;
         

        Thread ListenThread;


        public SendData[] PlayersSendData = new SendData[MAX_PLAYERS];

        public string GetMD5(string input)
        {
            MD5 md5 = System.Security.Cryptography.MD5.Create();
            byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
        }

        public void Start(string addr) 
        {
            ipAddr = IPAddress.Parse(addr);
            ipEndPoint = new IPEndPoint(ipAddr, 11000);
            sListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            sListener.Bind(ipEndPoint);

            ListenThread = new Thread(new ThreadStart(ListenRoutine));
            ListenThread.Start();
            Started = true;
        }

        private void ListenRoutine() 
        {


            while (true) 
            {

                if (PlayersActiveCount < MAX_PLAYERS)
                {
                    sListener.Listen(10);
                    PlayersSocketHandles[PlayersActiveCount] = sListener.Accept();
                    PlayersSocketHandles[PlayersActiveCount].Blocking = false;
                    PlayersActive[PlayersActiveCount] = true;
                    PlayersActiveCount++;
                }
                else 
                {
                    break;
                }
            }
        }

        public void Send(Socket PlayerSocket, SendData sd)
        {
            byte[] bytes = new byte[Marshal.SizeOf(typeof(SendData))];
            if (PlayerSocket != null)
                if (PlayerSocket.Connected)
                {
                    if (PlayerSocket.Poll(0, SelectMode.SelectWrite))
                    {
                        PlayerSocket.Send(sd.ToBytes());
                    }
                }
        }

        public void SendForAll()
        {
            // По  кол-ву активных игроков
            for (int i = 0; i < PlayersActiveCount; i++)
            {
                // если игрок существует
                if (PlayersActive[i])
                {
                    if (PlayersSocketHandles[i] != null)
                        if (PlayersSocketHandles[i].Connected)
                        {
                            // отошлем ему данные других активных игроков
                            for (int j = 0; j < PlayersActiveCount; j++) 
                            {
                                PlayersSendData[i].id_player = PlayersID[i];
                                PlayersSendData[i].count_player = PlayersActiveCount;

                                Send(PlayersSocketHandles[i], PlayersSendData[j]);
                            }
                        }
                }
            }
        }

        public void Receive(Socket PlayerSocket, out SendData sd)
        {
            sd = new SendData();

            byte[] bytes = new byte[Marshal.SizeOf(typeof(SendData))];

            if (PlayerSocket != null)
                if (PlayerSocket.Connected)
                    if (PlayerSocket.Poll(0, SelectMode.SelectRead))
                    {
                        int bytesRec = PlayerSocket.Receive(bytes);


                        sd.id_player = BitConverter.ToInt32(bytes, 0);
                        sd.x = BitConverter.ToInt32(bytes, 4);
                        sd.y = BitConverter.ToInt32(bytes, 8);
                        sd.count_player = BitConverter.ToInt32(bytes, 12);
                        sd.bird = BitConverter.ToInt32(bytes, 16);

                    }
        }

        public void ReceiveFromAll() 
        {
            for (int i = 0; i < PlayersActiveCount; i++) 
            {
                if (PlayersActive[i]) 
                {
                    if (PlayersSocketHandles[i] != null)
                    if (PlayersSocketHandles[i].Connected)
                    {
                        SendData sd = new SendData();

                        while (PlayersSocketHandles[i].Available > 1)
                        {
                            Receive(PlayersSocketHandles[i], out sd);
                        }

                        
                        PlayersSendData[i] = sd;

                        // Если это первый пакет от игрока. Инициализация ID
                        if (PlayersInitilized[i] == false) 
                        {
                            Random rnd = new Random((int)DateTime.Now.Ticks);
                            PlayersID[i] = rnd.Next(0, 1000);
                            PlayersInitilized[i] = true;
                        }

                    }
                }

            }
        
        }

        // Собирает все ip4 адреса в массив IP4HostList 
        public void LoadIPv4() 
        {
            IPHostEntry ipHost = Dns.GetHostEntry(string.Empty);
            //IPAddress ipAddr = null;

            int ip4Count  = 0;

            for (int i = 0; i < ipHost.AddressList.Length; i++)
            {
                if (ipHost.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
                {
                    ip4Count++; 
                }
            }

            IP4HostList = new IPAddress[ip4Count];
            int j = 0;

            for (int i = 0; i < ipHost.AddressList.Length; i++)
            {
                if (ipHost.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
                {
                    
                    IP4HostList[j] = ipHost.AddressList[i];
                    j++;
                }
            }
        }

        public int GetIPv4Count() 
        {

            return IP4HostList.Length;      
        }

        public string GetIPv4(int index) 
        {
            if (index < IP4HostList.Length) 
            {
                return IP4HostList[index].ToString();
            }
            return "";
        }

        public void ListenerThread() 
        {
        
        }


    }
}
